package com.camel.chart.func;

import com.camel.chart.enums.MsgTypeEnum;
import com.camel.chart.utils.FileUtil;
import com.camel.chart.utils.MsgUtil;
import com.camel.chart.utils.SerializeUtil;
import com.camel.chart.utils.entity.ApplicationConfig;
import com.camel.chart.utils.entity.ChartHistory;
import com.camel.chart.utils.entity.Msg;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollPane;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.UUID;

public class UdpClient implements Runnable {
    private final DatagramSocket datagramSocket;
    private final int port;

    private final Msg msg;

    private final InetAddress ip;

    private final Control control;

    // 定义构造方法 使其可以传入 DatagramSocket  和要访问的端口号，然后赋值给变量
    public UdpClient(DatagramSocket datagramSocket, int port, InetAddress ip, Msg msg, Control control) {
        this.datagramSocket = datagramSocket;
        this.port = port;
        this.ip = ip;
        this.msg = msg;
        this.control = control;
    }

    // 线程


    @Override
    public void run() {
        send();
    }

    public void send() {
        // 定义一个字符缓冲输入流
        BufferedInputStream bufferedInputStream = null;
        this.msg.setUuid(UUID.randomUUID().toString());
        try {
            // 把要访问的ip地址，端口号 和 数据放入
            msg.setDataTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            msg.setOriginAddr(ApplicationConfig.getInstance().getChartNetWorkInterface().getInetAddress());
            msg.setTargetAddr(ip.getHostAddress());
            // Platform.runLater 遵循FIFO  本地环境问题无法延时加载， 所以只有先更新本地UI
            switch (msg.getType()) {
                case TYPE_LOGIN:
                    sendMsg();
                    break;
                case TYPE_LOGIN_CALLBACK:
                    sendMsg();
                    break;
                case TYPE_TEXT_MSG:
                    System.err.println("请选择TCP1");
                    break;
                case TYPE_FILE_REQ:
                    MsgUtil.getInstance().addMsgUI((ScrollPane) control, msg);
                    sendAndSaveMsg();
                    break;
                case TYPE_LOGOUT:
                    sendMsg();
                    break;
                case TYPE_FILE_ACK:
                    MsgUtil.getInstance().updateMsgUI((ScrollPane) control, msg, true);
                    sendMsg();
                    break;
                case TYPE_FILE_MSG:
                    System.err.println("请选择TCP2");
                    break;
                default:
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            datagramSocket.close(); // 关闭资源
        }
    }


    void sendMsg(Msg m) throws IOException {
        DatagramPacket datagramPacket = new DatagramPacket(m.serialze(), m.serialze().length, ip, port);
        datagramSocket.send(datagramPacket);
    }

    void sendMsg() throws IOException {
        sendMsg(this.msg);
    }

    void sendAndSaveMsg(Msg msg) throws IOException {
        DatagramPacket datagramPacket = new DatagramPacket(msg.serialze(), msg.serialze().length, ip, port);
        datagramSocket.send(datagramPacket);
        if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
            ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
        }
        ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
        SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
    }

    void sendAndSaveMsg() throws IOException {
        sendMsg(this.msg);
        if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
            ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
        }
        ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
        SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
    }


    void sendFileAndSaveFile() throws IOException {
        FileUtil.FileEntity fileEntity = (FileUtil.FileEntity) this.msg.getMsg();

        byte[] msgByteArray = msg.serialze();
        int length = msgByteArray.length;

        ByteBuffer buffer = ByteBuffer.allocate(40960);
        int size = 0;
        try (FileChannel channel = FileChannel.open(Paths.get(fileEntity.getFilePath()), StandardOpenOption.READ)) {
            while (channel.read(buffer) != -1) {
                size++;
                buffer.flip();
                byte[] concatenate = concatenate(msgByteArray, buffer.array());
                DatagramPacket filePacket = new DatagramPacket(concatenate, concatenate.length, ip, port);
                datagramSocket.send(filePacket);
                Thread.sleep(22);
                buffer.clear();
            }
            // 发送结束
            Msg eof = new Msg();
            eof.setMsg(msg.getMsg());
            eof.setUuid(UUID.randomUUID().toString());
            eof.setType(MsgTypeEnum.TYPE_FILE_EOF);
            sendMsg(eof);
            if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
                ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
            }

            ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
            SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
        } catch (IOException fileNotFoundException) {
            fileNotFoundException.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }


        DatagramPacket filePacketPre = new DatagramPacket(this.msg.serialze(), this.msg.serialze().length, ip, port);
        datagramSocket.send(filePacketPre);


    }

    public static byte[] concatenate(byte[]... arrays) {
        int length = 0;
        for (byte[] array : arrays) {
            length += array.length;
        }
        byte[] result = new byte[length];
        int pos = 0;
        for (byte[] array : arrays) {
            pos += array.length;
        }
        return result;
    }
}
